package mapViewer;


import javax.microedition.lcdui.*;

import context.*;

import simpleMapService.*;

// ********************************************************************
// *** CLASS: MapLoader
// ********************************************************************
/**
*
*/
public class MapLoader implements Debug.Constants
{ //private static final boolean DEBUG=true; // Overriding Debug.Constants.DEBUG
    
  private FlexiMapService mapService=null;
  private Location mapCenter=null;
  private int imageWidth=300;//400;//800;
  private int imageHeight=375;//450;//900;
  private int scale=50000;
  private GeographicArea mapView;
  
  private static final int round(double x) { return((int)(x+0.5)); }
  
  // ******************************************************************
  // *** CONSTRUCTOR: MapLoader
  // ******************************************************************
  public MapLoader(Location mapCenter,int scale)
  { this.mapCenter=mapCenter;
    this.scale=scale;
    AccountRecord selectedAccount=AccountRecordStore.getSelectedAccount();
    String userID=selectedAccount.getUserID();
    String password=selectedAccount.getPassword();
    this.mapService=new FlexiMapService(userID,password);
    setMapView(mapCenter);
  }

  public Location getMapCenter() { return(mapCenter); }
  public int getScale() { return(scale); }
  public void setScale(int scale) { this.scale=scale; }
  public int getImageWidth() { return(imageWidth); }
  public int getImageHeight() { return(imageHeight); }
  
  // ******************************************************************
  // *** METHOD: getMap
  // ******************************************************************
  public Image getMap()
  { Image mapImage=null;
    try { mapImage=mapService.getMap(mapCenter,scale,imageWidth,imageHeight); }
    catch (Exception e)
    { if(DEBUG) Debug.EXCEPTION("MapLoader.getMap: getMap FAILED: ",e);
      mapImage=null;
    }
    if(DEBUG) Debug.println("MapLoader.getMap: mapImage: "+mapImage);
    if(mapImage==null)
    { try
      { if(DEBUG) Debug.println("MapLoader.getMap: TRY USING DEFAULT IMAGE");
//      mapImage = Image.createImage("/FlexiMap.png");
//      mapImage = Image.createImage("/SunMap.png");
        mapImage = Image.createImage("/NoConnection.png");
        if(DEBUG) Debug.println("*** TRY USING DEFAULT MAP -- OK: mapImage="+mapImage);
      }
      catch(Exception e)
      { Debug.EXCEPTION("MapLoader.getMap: TRY USING DEFAULT IMAGE -- FAILED: ",e); }
    }
    return(mapImage);
  }

  // ******************************************************************
  // *** METHOD: moveMapCenter
  // ******************************************************************
  /**
  * Move the map center location corresponding to the given pixel increments.
  * The pixel point is relative to the upper left coner of the image.
  * The result is calculated within the current map view.
  * <p>
  * @param dx the pixel increment in the x-direcion
  * @param dy the pixel increment in the y-direcion
  **/
  public void moveMapCenter(int dx,int dy,int scale)
  { double dx_InMeter= ((double)dx)*scale / mapService.pixelsPerWidthMeter();
    double dy_InMeter= ((double)dy)*scale / mapService.pixelsPerHeightMeter();
    if(DEBUG) Debug.println("dx_InMeter="+dx_InMeter+", dy_InMeter="+dy_InMeter);
//    { // TEST !!!
//      int px=dx+imageWidth/2;
//      int py=dy+imageHeight/2;
//      Debug.println("\n*** BEGIN TEST: px="+px+", py="+py);
//      Location newCenter=getLocation(new PixelPoint(px,py));
//      PixelPoint pp=getPixelPoint(newCenter);
//      Debug.println("*** END TEST: px="+pp.x+", py="+pp.y+'\n');
//    }
    
    mapCenter=mapCenter.move(dx_InMeter, dy_InMeter);
    setMapView(mapCenter);
  }

  // *** PRIVATE CONSTANTS ***
//  private final static int minScale=50; // 5 ??
  private final static double minWidth=0.001;
  private final static double minHeight=0.001;

  private void setMapView(Location center)//,Dimension imageSize,int scale)
  {
//    this.setImageSize(imageSize);
//    if(imageSize==null)  return;
//    if(imageSize.width==0)  return;
//    if(imageSize.height==0) return;
    GeographicArea area=new RectangularArea(center,minWidth,minHeight);
    mapView=justifyViewToBitmap(area);
//    setScale(scale);
  }

  // ******************************************************************
  // *** PRIVATE: justifyViewToBitmap
  // ******************************************************************
  private GeographicArea justifyViewToBitmap(GeographicArea view)
  { if(view==null)     return(null);
//    if(imageSize.width==0)  return(view);
//    if(imageSize.height==0) return(view);
    double width=view.getWidth();
    double height=view.getHeight();
    double W=imageWidth*2;  // 2==1/cos(60')
    double H=imageHeight;
    double wH=width*H;
    double hW=height*W;
    if(wH>hW) height=wH/W; else width=hW/H;
    return(new RectangularArea(view.getCenter(),width,height));
  }
  
  // ******************************************************************
  // *** METHOD: getPixelPoint
  // ******************************************************************
  /**
  * Get the pixel point corresponding to the given location.
  * The pixel point is relative to the upper left coner of the image.
  * The result is calculated within the current map view.
  * <p>
  * @param location a location on the screen
  * @return the pixel point
  **/
  public PixelPoint getPixelPoint(Location location)
  { double meanLat=(mapCenter.getLatitude()+location.getLatitude())/2.0;
    double oneLatDegree_InMeters=Location.oneLatDegree_InMeters;
    double oneLonDegree_InMeters=oneLatDegree_InMeters*Math.cos(Location.degreesToRadians(meanLat));
    double lonDiff=location.getLongitude()-mapCenter.getLongitude();
    double latDiff=location.getLatitude()-mapCenter.getLatitude();
    double lonDiff_InMeter=lonDiff*oneLonDegree_InMeters;
    double latDiff_InMeter=latDiff*oneLatDegree_InMeters;
    int dx=round(lonDiff_InMeter*mapService.pixelsPerWidthMeter()/scale);
    int dy=round(latDiff_InMeter*mapService.pixelsPerHeightMeter()/scale);
    int x=dx+imageWidth/2;
    int y=dy+imageHeight/2;
    PixelPoint pp=new PixelPoint(x,y);
    if(DEBUG) Debug.println("getPixelPoint("+location+") ==> "+pp);
    return(pp);
  }

  // ******************************************************************
  // *** METHOD: getPixelPoint (Original)
  // ******************************************************************
  /**
  * Get the pixel point corresponding to the given location.
  * The pixel point is relative to the upper left coner of the image.
  * The result is calculated within the current map view.
  * <p>
  * @param location a location on the screen
  * @return the pixel point
  **/
  public PixelPoint getPixelPoint2(Location location)
  { if(mapView==null) return(null);
    double degPerWidthPixel=mapView.getWidth()/imageWidth;
    int x=(int)((location.getLongitude()-mapView.getWest())/degPerWidthPixel);
    double degPerHeightPixel=mapView.getHeight()/imageHeight;
    int y=(int)((mapView.getNorth()-location.getLatitude())/degPerHeightPixel);
    return(new PixelPoint(x,y));
  }


  // ******************************************************************
  // *** METHOD: getLocation
  // ******************************************************************
  /**
  * Get the location corresponding to the given pixel point.
  * The pixel point is relative to the upper left coner of the image.
  * The result is calculated within the current map view.
  * <p>
  * @param pixelPoint a pixel point on the screen
  * @return the corresponding location
  **/
  public Location getLocation(PixelPoint pixelPoint)
  { double dx=pixelPoint.x-imageWidth/2;
    double dy=pixelPoint.y-imageHeight/2;
    double dx_InMeter=dx*scale/mapService.pixelsPerWidthMeter();
    double dy_InMeter=dy*scale/mapService.pixelsPerHeightMeter();
    Location newCenter=mapCenter.move(dx_InMeter, dy_InMeter);  
    if(DEBUG) Debug.println("getLocation("+pixelPoint+") ==> "+newCenter);
    return(newCenter);  
  }
  
  
  // ******************************************************************
  // *** METHOD: getLocation (Original)
  // ******************************************************************
  /**
  * Get the location corresponding to the given pixel point.
  * The pixel point is relative to the upper left coner of the image.
  * The result is calculated within the current map view.
  * <p>
  * @param p a pixel point on the screen
  * @return the corresponding location
  **/
  public Location getLocation2(PixelPoint p)
  { if(mapView==null) return(null);
    double lon=coordLongitudePixelToWGS84(p.x);
    double lat=coordLatitudePixelToWGS84(p.y);
    return(new Location(lon,lat));
  }  


  // ******************************************************************
  // *** PRIVATE: Coordinate transformations
  // ******************************************************************

  private double coordWGS84ToMMFX(double WGS84)
  { return (WGS84 * 0x1e848 - 0xffe17b80 - 0x3d0900) / 128; }

  private double coordWGS84ToMMFY(double WGS84)
  { return (WGS84 * 0x3d090 - 0xb71b00 - 0x3d0900) / 128; }

  private double coordMMFToWGS84X(double MMF)
  { return (128 * MMF + 0x3d0900) / 0x1e848 + -16; }

  private double coordMMFToWGS84Y(double MMF)
  { return (128 * MMF + 0x3d0900) / 0x3d090 + 48; }

  private double coordLongitudePixelToWGS84(int x)
  { if(mapView==null) return(0);
    double MMFwest = coordWGS84ToMMFX(mapView.getWest());
    double MMFeast = coordWGS84ToMMFX(mapView.getEast());
    double MMFcoordsperpixelx = Math.abs((MMFwest - MMFeast) / imageWidth);
    return coordMMFToWGS84X(MMFwest + x * MMFcoordsperpixelx);
  }

  private double coordLatitudePixelToWGS84(int y)
  { if(mapView==null) return(0);
    double MMFsouth = coordWGS84ToMMFY(mapView.getSouth());
    double MMFnorth = coordWGS84ToMMFY(mapView.getNorth());
    double MMFcoordsperpixely = Math.abs((MMFsouth - MMFnorth) / imageHeight);
    return coordMMFToWGS84Y(MMFnorth - y * MMFcoordsperpixely);
  }
  

}
